/*
 *  trace-recorder.h
 *  joosVM
 *
 *  Created by Smári Waage on 26.9.2009.
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA. *
 *	
 */

#ifndef trace_recoreder___ 
#define trace_recorder___ 



//On of trace feture 
#if not TRACE_JIT 
#define TRACE_REC(e) 
#define MAKE_CALLINFO(args...)

#else 

#include "nanojit-support.h"
/*
 Macro for trace recording, used to extend bytecode functions.
 */
#define TRACE_REC(e) if(Flags::recordTrace){TraceRecorder* rec = TraceRecorder::current; e checkNextLoopheader(s);}

/*
 Macro to blacklist a trace..
 */
#define BLACKLIST_TRACE dout(debug_trace_short,"blacklist\n")  rec->blacklist();
/*
 Macro to access the memory block that is into the trace. 
 */
#define mem_slotIndex(idx) (idx * sizeof(mem_slot))


class TraceRecorder;
class TraceTreeObject;


//The virtual table for the pointers.
typedef StaticArray Vtable;
typedef uint32_t mem_slot;
//For invocation of a trace as a function.
typedef union { NIns *code; GuardRecord* (FASTCALL *func)(mem_slot*, Vtable*); } code2Func;


//Types of exits for the trace loops.
enum ExitType{
	LoopExit,
	WrongBranch,
	WrongType,
	Loop,
	ExitToBranch,
	LevelGuard,
	NestedExit,
	WrongBranchBlacklist,
	ShouldNeverGetHere
};

enum RecordState{
	Compiled,
	BlackList,
	Canceled,
	Recording,
	Error
};


class TraceContext {
private:
	static TraceContext* inst;
	JSTraceMonitor* traceMonitor;
public:
	static TraceContext* getTraceContext();
	static JSTraceMonitor* getTraceMonitor();
protected:
	TraceContext();
	~TraceContext();
};

struct StoreInfo{
	LIns* current;
	bool localInit;
	bool write;
};

/*struct RestoreInfo{
	int			pc;

	int			level;
	int			storeStartIdx;
	int			storeSize;
	int			stackPos;
	int			stackSize;
	mem_slot	*stack;
	
	void		*method;
	RestoreInfo *prevLevel;
};
*/


struct JoosSideExit: SideExit{
	ExitType	exitType;
	JoosSideExit *next;
	
	JoosSideExit *nest;
	mem_slot	*nestStore; //Todo:save the space and store it somewhere else.
	
	int			pc;
	int			levelpos;
	int			storeStartIdx;
	int			storeSize;
	
	int			stackPos;
	int			method;
	
	mem_slot*	stack;
};


//To store info during recording.
struct LevelInfo{
	int			level;
	LevelInfo	*prevLevel;
	
	void		*method;
	int			storeStartIdx;
	int			storeSize;
	
	LIns		**stack;
	int			stackPos;
	int			stackMax;
	
	mem_slot	*stackStore;
};



/*
 To hold level(method) information during recording.
 */
class Level{
	static const int kInlineLevelMax = 10;
public:
	Level			*previous;
	
	TraceRecorder	*recorder;
	JoosSideExit	*levelExit;
	int				levelpos;
	JMethod		*method;
	
	int				storeStartIdx;
	StoreInfo		*store;
	int				storeSize;
	
	LIns			**stack;
	int				stackPos;
	int				stackSize;

	Level(JMethod* _method, Level* _previous, JoosSideExit* _levelExit, TraceRecorder* _recorder);
	~Level();
	
	void		push(LIns* v);
	LIns*		pop();
	
	LIns*		getStoreSlot(int i);
	void		putStoreSlot(int i, LIns* val);
	void		commitStore();
	void		clearStore();
	
	JoosSideExit* createSideExit(ExitType type);
	
};

/*
 Main responsible for recording.
 */
class TraceRecorder{
	static const int kInlineLevelMax = 10;
	static const int kTreeCallMax = 10;
	static const int kRecordAttemptMax = 10;
	int treeCallCount;
	
public:
	unsigned int		storeTotalSize;
	
	//not realy nice, todo:fix this global hack.
	static TraceRecorder *current;
	
	JoosSideExit		*_anchor;
	JSTraceMonitor		*traceMonitor;
	Fragment			*fragment;
	LirWriter		*lir;
	
	int					vtablepos;
	TraceTreeObject*	ttree;
	Level				*level;
	LIns				**pointerStore;
	
	LIns*				loophead;	
	
	TraceRecorder(JMethod* method, int startpc, int endpc, TraceTreeObject* tto);
	TraceRecorder( JMethod* method, JoosSideExit* anchor);
	~TraceRecorder();
	
	void	push(LIns* v);
	LIns*	pop();
	void	saveStack();
	
	LIns*	getStoreSlot(int i);
	void	putStoreSlot(int i, LIns* val);
	void	commitStore();
	void	clearStore();
	
	LIns*	insertPointer(HeapObject* o);
	int		insertPointerGetIndex(HeapObject* obj);
	
	bool	isBlackList();
	void	blacklist();
	
	void	pushLevel(JMethod* method, int invokeParac, bool _static, int pc);
	void	restoreLevel(JMethod* method, JoosSideExit* levelExit);
	void	popLevel(bool returnWithValue);
	
	GuardRecord*	createGuardRecord(JoosSideExit* exit);
	JoosSideExit*	createJoosSideExit(ExitType exitType);
	
	void			guard(bool expected, LIns* cond, int pc, int jump);
	JoosSideExit*	guard(bool expected, LIns* cond, ExitType typeType);
	
	void		compile();
	void		startRecord();
	void		closeRecord();
	void		cancelRecord();
	void		resetRecorder(JMethod* method);
	
	void		makeTreeCall(Fragment* f);
	
private:
	void		initRecorder(JMethod* method);
	LevelInfo*	initLevel(unsigned int stackSize, unsigned int storeSize, LevelInfo* parent, void* method);
	void		prelog();
	void		epilog();
};




/* Convinience for creating CallInfo, CallInfo is used by nanojit to make c function calls in trace. 
 MAKE_CALLINFO(
	"the name of function to create the call for, the name of the callinfo var will be name_ci", 
	"return type, ARGSIZE_I or ARGSIZE_NONE",
	"number of parameters, note that all are 32bit in size",
	"Call type ABI_STDCALL, ABI_FASTCALL..."
 */
#define DEF_CALL_0 
#define DEF_CALL_1 (nanojit::ARGSIZE_I << (1*nanojit::ARGSIZE_SHIFT))
#define DEF_CALL_2 (nanojit::ARGSIZE_I << (2*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (1*nanojit::ARGSIZE_SHIFT))
#define DEF_CALL_3 (nanojit::ARGSIZE_I << (3*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (2*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (1*nanojit::ARGSIZE_SHIFT))
#define DEF_CALL_4 (nanojit::ARGSIZE_I << (4*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (3*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (2*nanojit::ARGSIZE_SHIFT)) | (nanojit::ARGSIZE_I << (1*nanojit::ARGSIZE_SHIFT))
#define _EXPAND(tokens)  tokens
#define DEF_PARAMCALL(c)  _EXPAND(DEF_CALL_##c)

#define MAKE_CALLINFO(name, ret, paramc, type) const nanojit::CallInfo name##_ci = {(intptr_t) &name, DEF_PARAMCALL(paramc) | nanojit::ret,0,0,nanojit::type}
#define DECLARE_CALLINFO(name) static const nanojit::CallInfo name##_ci;


static int printout(void* i){
	printf("out: %p\n",i);
	return 1;
}
MAKE_CALLINFO(printout, ARGSIZE_I,1,ABI_STDCALL);
//const nanojit::CallInfo printout_ci = { (intptr_t) &printout, (nanojit::ARGSIZE_I << (1*nanojit::ARGSIZE_SHIFT)) | nanojit::ARGSIZE_I, 0, 0, nanojit::ABI_STDCALL };

#endif

#endif
